package com.lijin.batchsys.framework.control;

import java.util.Date;

import org.apache.log4j.Logger;

import com.lijin.batchsys.framework.common.ApplicationContextHolder;
import com.lijin.batchsys.framework.common.AssertUtil;
import com.lijin.batchsys.framework.common.BatchCache;
import com.lijin.batchsys.framework.common.BatchSysException;
import com.lijin.batchsys.framework.common.BatchUtil;
import com.lijin.batchsys.framework.common.IBatchLogger;
import com.lijin.batchsys.framework.dao.model.BatchInfo;
import com.lijin.batchsys.framework.dao.model.JobInfo;
import com.lijin.batchsys.framework.dao.model.StepInfo;
import com.lijin.batchsys.framework.enums.RunningMode;
import com.lijin.batchsys.framework.enums.RunningStatus;

public class BatchControl {

	private static final Logger logger = Logger.getLogger(BatchControl.class);

	/**
	 * 拉起指定的批次
	 * @param batchRunningMode - 批次执行模式：0-整个批次;1-单任务;2-单步骤
	 * @param batchDateStr - 批次日期
	 * @param batchNo - 批次号
	 * @param jobNo - 任务号: 如果是拉起整个批次时，任务号不起作用
	 * @param stepNo - 步骤号: 如果是拉起整个批次和单个任务时，步骤号不起作用
	 */
	public static void startBatch(RunningMode batchRunningMode, 
			String batchDateStr, int batchNo, int jobNo, int stepNo) {
		
		String runningInfo = batchRunningMode.getDesc() + "_" + batchNo + "_" + jobNo + "_" + stepNo + ":" + batchDateStr;
		logger.info("批次开始: " + runningInfo);
		
		BatchExecContext context = null;
		
		try {
			// 解析日期字符串，如果不能解析为日期，则拉起批次报错
			Date batchDate = BatchUtil.parse(batchDateStr);
			
			// 构建待执行的批次信息
			BatchInfo runningBatchInfo = buildRunningBatch(batchNo, jobNo, stepNo, batchDateStr, batchRunningMode);
			if (runningBatchInfo.getStepNum() == 0) {
				throw new BatchSysException("该批次无可执行步骤:" + runningInfo);
			}
			
			// 构造批量执行上下文信息
			context = new BatchExecContext(batchRunningMode, runningBatchInfo, batchDate);
			IBatchLogger batchLogger = ApplicationContextHolder.getBean("batchLogger");
			context.setBatchLogger(batchLogger);
			context.logContext();
			
			// 执行批次
			runBatch(context);

			// 更新上下文状态，并记录
			context.setStatus(RunningStatus.COMPLETED);
			context.setExecMsg("执行成功");
			
			logger.info("批次完成: " + runningInfo);
		} catch (Exception e) {
			if (e instanceof BatchSysException) {
				logger.error(e.getMessage(), e);
			} else {
				logger.error("批次出错:" + runningInfo, e);
			}
			
			if (context != null) {
				context.setStatus(RunningStatus.FAILED);
			}
		} finally {
			if (context != null) {
				context.logContext();				
			}
		}
	}
	
	/**
	 * 构造待执行的批次列表
	 * 
	 * @param batchNo
	 * @param jobNo
	 * @param stepNo
	 * @return
	 */
	private static BatchInfo buildRunningBatch(int batchNo, int jobNo, int stepNo,
			String batchDateStr, RunningMode runningMode) {
		
		BatchInfo batch = BatchCache.getInstance().getBatchInfo(batchNo);
		AssertUtil.objIsNull(batch, "未找到指定的批次:" + batchNo);

		logger.info("构造执行批次信息开始..");
		
		// 根据缓存信息构造本次要执行的批次信息
		BatchInfo runningBatchInfo = new BatchInfo();
		runningBatchInfo.setBatchNo(batch.getBatchNo());
		runningBatchInfo.setBatchDesc(batch.getBatchDesc());
		for (JobInfo jobInfo : batch.getJobList()) {
			// 如果是拉起单个任务模式或拉起单个步骤模式，则判断是否是指定要执行的任务，如果不是则跳过
			if (runningMode == RunningMode.START_SINGLE_JOB 
					|| runningMode == RunningMode.START_SINGLE_STEP) {
				if (jobNo != jobInfo.getJobNo()) {
					continue;
				}
			}
			
			// 根据任务的配置信息判断该任务是否被启用，如果是，则列入待执行列表
			JobInfo runningJobInfo = new JobInfo();
			runningJobInfo.setId(jobInfo.getId());
			runningJobInfo.setBatchNo(jobInfo.getBatchNo());
			runningJobInfo.setBatchDesc(jobInfo.getBatchDesc());
			runningJobInfo.setJobNo(jobInfo.getJobNo());
			runningJobInfo.setJobDesc(jobInfo.getJobDesc());
			runningJobInfo.setJobCtrlFlgs(jobInfo.getJobCtrlFlgs());
			for (StepInfo stepInfo : jobInfo.getStepList()) {
				// 如果是拉起单个步骤模式，则判断是否是指定要执行的步骤，如果不是则跳过
				if (runningMode == RunningMode.START_SINGLE_STEP) {
					if (stepNo != stepInfo.getStepNo()) {
						continue;
					}
				}
				
				// 判断是步骤是否还在执行，如果是，则批次拉起失败
				BatchSysTask stepTask = ApplicationContextHolder.getBean(stepInfo.getStepBeanName());
				if (stepTask.isRunning()) {
					throw new BatchSysException("步骤" + stepInfo.getSummary() + "正在执行，批次拉起失败");
				}

				// 判断步骤可以在指定的日期执行
				if (stepInfo.checkRunDate(batchDateStr)) {
					StepInfo runningStepInfo = new StepInfo();
					runningStepInfo.setId(stepInfo.getId());
					runningStepInfo.setBatchNo(stepInfo.getBatchNo());
					runningStepInfo.setBatchDesc(stepInfo.getBatchDesc());
					runningStepInfo.setJobNo(stepInfo.getJobNo());
					runningStepInfo.setJobDesc(stepInfo.getJobDesc());
					runningStepInfo.setStepNo(stepInfo.getStepNo());
					runningStepInfo.setStepDesc(stepInfo.getStepDesc());
					runningStepInfo.setStepBeanName(stepInfo.getStepBeanName());
					runningStepInfo.setStepCtrlFlgs(stepInfo.getStepCtrlFlgs());
					runningJobInfo.addStep(runningStepInfo);
				} else {
					logger.info("步骤[" + stepInfo.getSummary() + "]不能在该批量日期执行:" + batchDateStr);
				}
			}

			// 将任务待执行的步骤数大于0，则列入待执行批次的任务列表
			if (runningJobInfo.getStepNum() > 0) {
				runningBatchInfo.addJob(runningJobInfo);
			}
		}

		logger.info("构造执行批次信息完成");
		// 打印执行批次信息
		StringBuilder logInfo = new StringBuilder("\n******************************************\n");
		logInfo.append("执行批次信息：\n");
		logInfo.append("批次号：" + runningBatchInfo.getBatchNo() + "\n");
		logInfo.append("批次名称：" + runningBatchInfo.getBatchDesc() + "\n");
		logInfo.append("拉起时间：" + BatchUtil.formatTimeStamp(new Date()) + "\n");
		logInfo.append("执行方式：" + runningMode.getDesc() + "\n");
		logInfo.append("执行任务列表:\n" + runningBatchInfo.getBatchDetail() + "\n");
		logInfo.append("******************************************");
		logger.info(logInfo.toString());

		return runningBatchInfo;
	}
	
	/**
	 * 逐个执行批次的各个任务
	 * @param batchContext
	 */
	private static void runBatch(BatchExecContext batchContext) {
		BatchInfo runningBatchInfo = batchContext.getRunningBatchInfo();
		for (JobInfo jobInfo : runningBatchInfo.getJobList()) {
			String currJobMsg = "当前任务:" + jobInfo.getJobNo() + "-" + jobInfo.getJobDesc();
			batchContext.setExecMsg(currJobMsg);
			batchContext.logContext();
			// 根据Job配置信息构建待执行的Job实体
			IBatchSysJob job = BatchSysJobFactory.buildBatchSysJob(jobInfo);
			job.execute(batchContext);
		}
	}
}
